#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>

using namespace std;

#include "EasyBMP.h"
#include "EasyBMP_Geometry.h"
#include "EasyBMP_Font.h"

// #include "misc.h"
#include "ImprovedMath.h"

RGBApixel GrayscaleFunction( double min, double value, double max )
{
 RGBApixel Output;
 double ScaledValue = ( value - min )/(max-min+1e-16);
 if( ScaledValue < 0.0 )
 { ScaledValue = 0.0; }
 if( ScaledValue > 1.0 )
 { ScaledValue = 1.0; }

 ebmpBYTE ColorValue = (ebmpBYTE) ImprovedRound( ScaledValue * 255.0 ); 
 Output.Red   = ColorValue;
 Output.Green = ColorValue;
 Output.Blue  = ColorValue;

 return Output;
}

RGBApixel RainbowFunction( double min, double value, double max )
{
 RGBApixel Output;
 double ScaledValue = ( value - min )/(max-min+1e-16);
 if( ScaledValue < 0.0 )
 { ScaledValue = 0.0; }
 if( ScaledValue > 1.0 )
 { ScaledValue = 1.0; }
 
 RGBApixel output;
 double t = ScaledValue;

 double red = 0;
 double green = 0;
 double blue = 0;
 
 if( -0.25 <= t && t < 0.25 ) 
 {
  double s = t+0.25;
  blue = sqrt( s/0.50 + 1e-16 );
 }
 if( 0.25 <= t && t <= 0.75 )
 {
  double s = t - 0.25;
  blue = sqrt( (0.5-s)/0.50 + 1e-16 );
 }
 
 if( 0 <= t && t < 0.50 ) 
 {
  double s = t;
  green = sqrt( s/0.50 + 1e-16 );
 }
 if( 0.50 <= t && t <= 1.0 )
 {
  double s = t - 0.50;
  green = sqrt( (0.5-s)/0.50 + 1e-16 );
 }
 
 if( 0.25 <= t && t < 0.75 ) 
 {
  double s = t-0.25;
  red = sqrt( s/0.50 + 1e-16 );
 }
 if( 0.75 <= t && t <= 1.25 )
 {
  double s = t - 0.75;
  red = sqrt( (0.5-s)/0.50 + 1e-16 );
 }

 output.Red = (ebmpBYTE) (red*255.0);
 output.Green = (ebmpBYTE) (green*255.0);
 output.Blue = (ebmpBYTE) (blue *255.0);

 return output;
}

RGBApixel BlueRedFunction( double min, double value, double max )
{
 RGBApixel Output;
 double ScaledValue = ( value - min )/(max-min+1e-16);
 if( ScaledValue < 0.0 )
 { ScaledValue = 0.0; }
 if( ScaledValue > 1.0 )
 { ScaledValue = 1.0; }

 ebmpBYTE ColorValue = (ebmpBYTE) ImprovedRound( ScaledValue * 255.0 ); 
 Output.Red   = ColorValue;
 Output.Green = 0;
 Output.Blue  = 255-ColorValue;

 return Output;
}

RGBApixel BlendColorsLinearAddition( RGBApixel Color1, double Fraction1, 
                                     RGBApixel Color2, double Fraction2 )
{
 RGBApixel Output;
 Output.Red   = (ebmpBYTE) ImprovedRound( Color1.Red * Fraction1 + Color2.Red * Fraction2 );
 Output.Green = (ebmpBYTE) ImprovedRound( Color1.Green * Fraction1 + Color2.Green * Fraction2 );
 Output.Blue  = (ebmpBYTE) ImprovedRound( Color1.Blue * Fraction1 + Color2.Blue * Fraction2 );
 return Output;
}

RGBApixel BlendColorsMultiplication( RGBApixel Color1, RGBApixel Color2)
{
 RGBApixel Output;
 Output.Red   = (ebmpBYTE) ImprovedRound( (double) Color1.Red   * (double) Color2.Red / 255.0 );
 Output.Green = (ebmpBYTE) ImprovedRound( (double) Color1.Green * (double) Color2.Green / 255.0 );
 Output.Blue  = (ebmpBYTE) ImprovedRound( (double) Color1.Blue  * (double) Color2.Blue / 255.0 );
 return Output;
}

bool DrawFrameBorder( BMP& Input )
{
 extern RGBApixel Black;
 
 for( int i=0 ; i < Input.TellWidth() ; i++ )
 {
  *( (Input)(i,0) ) = Black;
  *( (Input)(i,Input.TellHeight()-1) ) = Black;
 }

 for( int j=1 ; j < Input.TellHeight()-1 ; j++ )
 {
  *( (Input)(0,j) ) = Black;
  *( (Input)(Input.TellWidth()-1,j) ) = Black;
 }

 return true;
}

/*
RGBApixel ColorPixel( int i, int j )
{
 RGBApixel Output;
 
 extern RGBApixel White;
 
 Output = White;
 
 extern int Ynodes;
 
 int I = i;
 int J = Ynodes-j-1;
 
 extern Matrix Phi;
 extern Matrix PhiN;
 extern Matrix PhiQ;
  
 double phi = *Phi(I,J);
 double phiN = *PhiN(I,J);
 double phiQ = *PhiQ(I,J);

 double blood;
 double sprout; 
 
 extern bool PlotCapillaries;
 
 if( PlotCapillaries )
 {
  extern Matrix BloodVesselIndicator;
  extern Matrix SproutTips;
 
  blood = *BloodVesselIndicator(I,J);
  if( SproutTips.TellRows() > 1 )
  { sprout = *SproutTips(I,J); }
  else
  { sprout = -1; }
 }
 else
 {
  blood = -1;
  sprout = -1; 
 }
 
 bool Done = false;
 
 extern double _EPS;
 extern RGBApixel NecroticTumorColor;
 extern RGBApixel HypoxicTumorColor;
 extern RGBApixel TumorColor;
 
 if( phiN <= _EPS )
 {
  Output = NecroticTumorColor;
  Done = true;
 }
 if( phiQ <= _EPS && !Done )
 {
  Output = HypoxicTumorColor;
  Done = true;
 }
 if( phi <= _EPS && !Done )
 { 
  Output = TumorColor;
 }

 if( PlotCapillaries )
 {
  extern RGBApixel CapillaryColor;
  extern RGBApixel SproutTipColor;
 
  if( blood > 0.25 )
  {
   RGBApixel TempCapillaryColor;
   TempCapillaryColor.Red = (ebmpBYTE) ( blood*(CapillaryColor.Red-255) + 255 );
   TempCapillaryColor.Green = (ebmpBYTE) ( blood*(CapillaryColor.Green-255) + 255 );
   TempCapillaryColor.Blue = (ebmpBYTE) ( blood*(CapillaryColor.Blue-255) + 255 );
   
   Output.Red = (ebmpBYTE) ( (Output.Red * TempCapillaryColor.Red/255.0 ) );
   Output.Green = (ebmpBYTE) ( (Output.Green * TempCapillaryColor.Green/255.0  ) );
   Output.Blue = (ebmpBYTE) ( (Output.Blue * TempCapillaryColor.Blue/255.0  ) );
  }
  
  if( sprout > 0.25 )
  {
   Output = SproutTipColor;
  }
  
 }
 
 return Output;
}
*/

/*
bool DrawTumorAndBloodVessels( BMP& Window )
{
 for( int j=0; j < Window.TellHeight() ; j++ )
 {
  for( int i=0; i < Window.TellWidth() ; i++ )
  {
   *Window(i,j) = ColorPixel(i,j);
  }
 }
 return true;
}
*/

bool OverlayImage_MultiplicationBlending( BMP& To , BMP& From )
{
 for( int i=0 ; i < To.TellWidth() ; i++ )
 {
  for( int j=0 ; j < To.TellHeight() ; j++ )
  {
   *To(i,j) = BlendColorsMultiplication( *From(i,j) , *To(i,j) );
  }
 } 
 return true;
}

bool OverlayImage_MultiplicationBlending( BMP& To , BMP& From, double BlendParameter )
{
 for( int i=0 ; i < To.TellWidth() ; i++ )
 {
  for( int j=0 ; j < To.TellHeight() ; j++ )
  {
   RGBApixel Blended = BlendColorsMultiplication( *From(i,j) , *To(i,j) );
   RGBApixel Solid   = *From(i,j);
   
   if( Solid.Red != 255 || Solid.Green != 255 || Solid.Blue != 255 )
   {
    *To(i,j) = BlendColorsLinearAddition( Solid  , BlendParameter,  
                                          Blended, 1.0-BlendParameter );
   }
   
  }
 } 
 return true;
}

bool OverlayImage_ReplaceTransparent( BMP& To , BMP& From )
{
 for( int i=0 ; i < To.TellWidth() ; i++ )
 {
  for( int j=0 ; j < To.TellHeight() ; j++ )
  {
   if( From(i,j)->Red   != 255   || 
       From(i,j)->Green != 255 || 
	   From(i,j)->Blue  != 255 )
   { *To(i,j) = *From(i,j); } 
  }
 } 
 return true;
}

bool DrawGrayscaleColorbar( BMP& Input, double min, double max, 
                            int TopLeftX, int TopLeftY, int Width, int Height )
{
 for( int j=TopLeftY+1 ; j <= TopLeftY+Height-1 ; j++ )
 {
  RGBApixel TempColor;
  if( min < max )
  { TempColor = GrayscaleFunction( 1, TopLeftY+Height-j, Height-1 ); }
  else
  { TempColor = GrayscaleFunction( Height-1, TopLeftY+Height-j, 1 ); }
  for( int i=TopLeftX+1 ; i <= TopLeftX+Width-1 ; i++ )
  { *Input(i,j) = TempColor; }
 }
 
 RGBApixel Black;
 Black.Red   = 0;
 Black.Green = 0;
 Black.Blue  = 0;

 for( int j = TopLeftY ; j < TopLeftY + Height ; j++ )
 {
  *Input(TopLeftX,j) = Black;
  *Input(TopLeftX+Width,j) = Black;
 }
 for( int i = TopLeftX ; i <= TopLeftX + Width ; i++ )
 {
  *Input(i,TopLeftY) = Black;
  *Input(i,TopLeftY+Height-1) = Black;
 } 
 
 char str_min [1024];
 char str_max [1024];
 
 if( min < max )
 {
  sprintf( str_min, "%3.2f", min );
  if( fabs( min ) < 0.01 && fabs( min ) > 1e-10 )
  {
   sprintf( str_min, "%1.1e", min );
  }
//  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, Black );
  
  sprintf( str_max, "%3.2f", max );
  if( fabs( max ) < 0.01 && fabs( max ) > 1e-10 )
  {
   sprintf( str_max, "%1.1e", max );
  }
  
// PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 }
 else
 {
  sprintf( str_max, "%3.2f", min );
  if( fabs( min ) < 0.01 )
  {
   sprintf( str_max, "%1.1e", min );
  }
  
//  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, Black );
  
  sprintf( str_min, "%3.2f", max );
  if( fabs( max ) < 0.01 )
  {
   sprintf( str_min, "%1.1e", min );
  }
  
//  PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 }
 
 PrintString( Input, str_max, TopLeftX+2,TopLeftY-10-3, 10, Black );
 PrintString( Input, str_min, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 
/* 
 char str [1024];
 extern RGBApixel FontColor;
 if( min < max )
 {
  sprintf( str, "%3.2f", max );
  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, FontColor ); // Black );
  
  sprintf( str, "%3.2f", min );
  PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, FontColor ); // Black );
 }
 else
 {
  sprintf( str, "%3.2f", min );
  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, FontColor ); // Black );
  
  sprintf( str, "%3.2f", max );
  PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, FontColor ); // Black );
 }
*/
 return true;
}

bool DrawBlueRedColorbar( BMP& Input, double min, double max, 
                          int TopLeftX, int TopLeftY, int Width, int Height )
{
 for( int j=TopLeftY+1 ; j <= TopLeftY+Height-1 ; j++ )
 {
  RGBApixel TempColor;
  if( min < max )
  { TempColor = BlueRedFunction( 1, TopLeftY+Height-j, Height-1 ); }
  else
  { TempColor = BlueRedFunction( Height-1, TopLeftY+Height-j, 1 ); }
  for( int i=TopLeftX+1 ; i <= TopLeftX+Width-1 ; i++ )
  {
   *Input(i,j) = TempColor;
  }
 }
 
 RGBApixel Black;
 Black.Red   = 0;
 Black.Green = 0;
 Black.Blue  = 0;

 for( int j = TopLeftY ; j < TopLeftY + Height ; j++ )
 {
  *Input(TopLeftX,j) = Black;
  *Input(TopLeftX+Width,j) = Black;
 }
 for( int i = TopLeftX ; i <= TopLeftX + Width ; i++ )
 {
  *Input(i,TopLeftY) = Black;
  *Input(i,TopLeftY+Height-1) = Black;
 } 
 
 char str_min [1024];
 char str_max [1024];
 
 if( min < max )
 {
  sprintf( str_min, "%3.2f", min );
  if( fabs( min ) < 0.01 && fabs( min ) > 1e-10 )
  {
   sprintf( str_min, "%1.1e", min );
  }
//  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, Black );
  
  sprintf( str_max, "%3.2f", max );
  if( fabs( max ) < 0.01 && fabs( max ) > 1e-10 )
  {
   sprintf( str_max, "%1.1e", max );
  }
  
// PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 }
 else
 {
  sprintf( str_max, "%3.2f", min );
  if( fabs( min ) < 0.01 )
  {
   sprintf( str_max, "%1.1e", min );
  }
  
//  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, Black );
  
  sprintf( str_min, "%3.2f", max );
  if( fabs( max ) < 0.01 )
  {
   sprintf( str_min, "%1.1e", min );
  }
  
//  PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 }
 
 PrintString( Input, str_max, TopLeftX+2,TopLeftY-10-3, 10, Black );
 PrintString( Input, str_min, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 
/* 
 char str [1024];
 if( min < max )
 {
  sprintf( str, "%3.2f", max );
  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, Black );
  
  sprintf( str, "%3.2f", min );
  PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 }
 else
 {
  sprintf( str, "%3.2f", min );
  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, Black );
  
  sprintf( str, "%3.2f", max );
  PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 }
*/
 return true;
}

bool DrawRainbowColorbar( BMP& Input, double min, double max, 
                          int TopLeftX, int TopLeftY, int Width, int Height )
{
 for( int j=TopLeftY+1 ; j <= TopLeftY+Height-1 ; j++ )
 {
  RGBApixel TempColor;
  if( min < max )
  { TempColor = RainbowFunction( 1, TopLeftY+Height-j, Height-1 ); }
  else
  { TempColor = RainbowFunction( Height-1 , TopLeftY+Height-j, 1); }
  for( int i=TopLeftX+1 ; i <= TopLeftX+Width-1 ; i++ )
  { *Input(i,j) = TempColor; }
 }
 
 RGBApixel Black;
 Black.Red   = 0;
 Black.Green = 0;
 Black.Blue  = 0;

 for( int j = TopLeftY ; j < TopLeftY + Height ; j++ )
 {
  *Input(TopLeftX,j) = Black;
  *Input(TopLeftX+Width,j) = Black;
 }
 for( int i = TopLeftX ; i <= TopLeftX + Width ; i++ )
 {
  *Input(i,TopLeftY) = Black;
  *Input(i,TopLeftY+Height-1) = Black;
 } 
 
 char str_min [1024];
 char str_max [1024];
 
 if( min < max )
 {
  sprintf( str_min, "%3.2f", min );
  if( fabs( min ) < 0.01 && fabs( min ) > 1e-10 )
  {
   sprintf( str_min, "%1.1e", min );
  }
//  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, Black );
  
  sprintf( str_max, "%3.2f", max );
  if( fabs( max ) < 0.01 && fabs( max ) > 1e-10 )
  {
   sprintf( str_max, "%1.1e", max );
  }
  
// PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 }
 else
 {
  sprintf( str_max, "%3.2f", min );
  if( fabs( min ) < 0.01 )
  {
   sprintf( str_max, "%1.1e", min );
  }
  
//  PrintString( Input, str, TopLeftX+2,TopLeftY-10-3, 10, Black );
  
  sprintf( str_min, "%3.2f", max );
  if( fabs( max ) < 0.01 )
  {
   sprintf( str_min, "%1.1e", min );
  }
  
//  PrintString( Input, str, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 }
 
 PrintString( Input, str_max, TopLeftX+2,TopLeftY-10-3, 10, Black );
 PrintString( Input, str_min, TopLeftX+2,TopLeftY+Height+2 , 10, Black );
 
 return true;
}

bool CopyImage( BMP& To, BMP& From )
{
 To.SetSize( From.TellWidth() , From.TellHeight() );
 for( int j=0; j < To.TellHeight() ; j++ )
 {
  for( int i=0 ; i < To.TellWidth() ; i++ )
  { *To(i,j) = *From(i,j); }
 }
 return true;
}

bool ClearImage( BMP& Input , RGBApixel ClearColor )
{
 for( int j=0; j < Input.TellHeight() ; j++ )
 {
  for( int i=0; i < Input.TellWidth() ; i++ )
  {
   *Input(i,j) = ClearColor;  
  }
 }

 return true;
}

bool Rotate90( BMP& Input )
{
 BMP Temp( Input );
 Input.SetSize( Temp.TellHeight() , Temp.TellWidth() );

 for( int j=0 ; j < Temp.TellHeight() ; j++ )
 {
  for( int i=0; i < Temp.TellWidth() ; i++ )
  {
   *Input(Temp.TellHeight()-1-j,i) = *Temp(i,j);  
  }
 }
 
 return true;
}

bool PlotLengthBar( BMP& Input, int TopLeftX, int TopLeftY, int Width, int Height , char* Label )
{
 extern RGBApixel Black;
 extern RGBApixel White;
 extern RGBApixel FrameColor;
 
 for( int i=0 ; i < Width/5 ; i++ )
 {
  for( int j=0; j < Height ; j++ )
  {
   *Input( TopLeftX+i , TopLeftY+j) = Black;
  }
 }
 
/*
 for( int i=0 ; i < Width ; i++ )
 {
  *Input(TopLeftX+i,TopLeftY) = Black;
  *Input(TopLeftX+i,TopLeftY+Height-1) = Black;
 }
 
 for( int j=0 ; j < Height ; j++ )
 {
  *Input( TopLeftX + Width/2 , TopLeftY+j) = Black;
 }
*/ 
 
 int TempFontSize = (Width*3)/5;
 
 BMP Temp;
 Temp.SetSize( Width*3 , TempFontSize+1 ); // Width*3/4 );
 ClearImage( Temp, FrameColor );
 
  
 PrintString( Temp, Label, 0,0, TempFontSize, Black );
/* 
 PrintString( Temp, Label, 0,0, (Width*3)/4, Black );
*/ 
  
 Rotate90( Temp );

 RangedPixelToPixelCopyTransparent( Temp,0,Temp.TellWidth()-1,0,Temp.TellHeight()-1, 
                                    Input, TopLeftX+(2*Width)/5 , TopLeftY , 
									FrameColor );
/*									
 RangedPixelToPixelCopyTransparent( Temp,0,Temp.TellWidth()-1,0,Temp.TellHeight()-1, 
                                    Input, TopLeftX+Width/2 , TopLeftY+Height/2-Temp.TellHeight()/2 , 
									FrameColor );
*/									
 
 return true;
}
